home *** CD-ROM | disk | FTP | other *** search
- /* User calls to TCP */
- #include <stdio.h>
- #include "global.h"
- #include "timer.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "socket.h"
- #include "internet.h"
- #include "tcp.h"
- #include "ip.h"
- #include "icmp.h"
- #include "proc.h"
-
- int16 Tcp_window = DEF_WND;
-
- struct tcb *
- open_tcp(lsocket,fsocket,mode,window,r_upcall,t_upcall,s_upcall,tos,user)
- struct socket *lsocket; /* Local socket */
- struct socket *fsocket; /* Remote socket */
- int mode; /* Active/passive/server */
- int16 window; /* Receive window (and send buffer) sizes */
- void (*r_upcall)(); /* Function to call when data arrives */
- void (*t_upcall)(); /* Function to call when ok to send more data */
- void (*s_upcall)(); /* Function to call when connection state changes */
- int tos;
- int user; /* User linkage area */
- {
- struct connection conn;
- struct tcb *tcb;
-
- if(lsocket == NULLSOCK){
- Net_error = INVALID;
- return NULLTCB;
- }
- conn.local.address = lsocket->address;
- conn.local.port = lsocket->port;
- if(fsocket != NULLSOCK){
- conn.remote.address = fsocket->address;
- conn.remote.port = fsocket->port;
- } else {
- conn.remote.address = 0;
- conn.remote.port = 0;
- }
- if((tcb = lookup_tcb(&conn)) == NULLTCB){
- if((tcb = create_tcb(&conn)) == NULLTCB){
- Net_error = NO_MEM;
- return NULLTCB;
- }
- } else if(tcb->state != TCP_LISTEN){
- Net_error = CON_EXISTS;
- return NULLTCB;
- }
- tcb->user = user;
- if(window != 0)
- tcb->window = tcb->rcv.wnd = window;
- else
- tcb->window = tcb->rcv.wnd = Tcp_window;
- tcb->snd.wnd = 1; /* Allow space for sending a SYN */
- tcb->r_upcall = r_upcall;
- tcb->t_upcall = t_upcall;
- tcb->s_upcall = s_upcall;
- tcb->tos = tos;
- switch(mode){
- case TCP_SERVER:
- tcb->flags.clone = 1;
- case TCP_PASSIVE: /* Note fall-thru */
- setstate(tcb,TCP_LISTEN);
- break;
- case TCP_ACTIVE:
- /* Send SYN, go into TCP_SYN_SENT state */
- tcb->flags.active = 1;
- send_syn(tcb);
- setstate(tcb,TCP_SYN_SENT);
- tcp_output(tcb);
- break;
- }
- return tcb;
- }
-
- /* User send routine */
- int
- send_tcp(struct tcb *tcb,struct mbuf *bp)
- {
- int16 cnt = len_p(bp);
-
- if(tcb == NULLTCB || bp == NULLBUF){
- if(bp)
- free_p(bp);
- Net_error = INVALID;
- return -1;
- }
-
- switch(tcb->state){
- case TCP_CLOSED:
- free_p(bp);
- Net_error = NO_CONN;
- return -1;
- case TCP_LISTEN:
- if(tcb->conn.remote.address == 0 && tcb->conn.remote.port == 0){
- /* Save data for later */
- append(&tcb->sndq,bp);
- tcb->sndcnt += cnt;
- break;
- }
- /* Change state from passive to active */
- tcb->flags.active = 1;
- send_syn(tcb);
- setstate(tcb,TCP_SYN_SENT); /* Note fall-thru */
- case TCP_SYN_SENT:
- case TCP_SYN_RECEIVED:
- case TCP_ESTABLISHED:
- case TCP_CLOSE_WAIT:
- append(&tcb->sndq,bp);
- tcb->sndcnt += cnt;
- tcp_output(tcb);
- break;
- case TCP_FINWAIT1:
- case TCP_FINWAIT2:
- case TCP_CLOSING:
- case TCP_LAST_ACK:
- case TCP_TIME_WAIT:
- free_p(bp);
- Net_error = CON_CLOS;
- return -1;
- }
- return (int)cnt;
- }
-
- /* User receive routine */
- int
- recv_tcp(struct tcb *tcb,struct mbuf **bpp,int16 cnt)
- {
- if(tcb == NULLTCB || bpp == (struct mbuf **)NULL){
- Net_error = INVALID;
- return -1;
- }
- if(tcb->rcvcnt == 0){
- /* If there's nothing on the queue, our action depends on what state
- * we're in (i.e., whether or not we're expecting any more data).
- * If no more data is expected, then simply return 0; this is
- * interpreted as "end of file". Otherwise return -1.
- */
- switch(tcb->state){
- case TCP_LISTEN:
- case TCP_SYN_SENT:
- case TCP_SYN_RECEIVED:
- case TCP_ESTABLISHED:
- case TCP_FINWAIT1:
- case TCP_FINWAIT2:
- Net_error = WOULDBLK;
- return -1;
- case TCP_CLOSED:
- case TCP_CLOSE_WAIT:
- case TCP_CLOSING:
- case TCP_LAST_ACK:
- case TCP_TIME_WAIT:
- *bpp = NULLBUF;
- return 0;
- }
- }
- /* cnt == 0 means "I want it all" */
- if(cnt == 0)
- cnt = tcb->rcvcnt;
- /* See if the user can take all of it */
- if(tcb->rcvcnt <= cnt){
- cnt = tcb->rcvcnt;
- *bpp = tcb->rcvq;
- tcb->rcvq = NULLBUF;
- } else {
- *bpp = ambufw(cnt);
- pullup(&tcb->rcvq,(*bpp)->data,cnt);
- (*bpp)->cnt = cnt;
- }
- tcb->rcvcnt -= cnt;
- tcb->rcv.wnd += cnt;
- /* Do a window update if it was closed */
- if(cnt == tcb->rcv.wnd){
- tcb->flags.force = 1;
- tcp_output(tcb);
- }
- return (int)cnt;
- }
-
- /* This really means "I have no more data to send". It only closes the
- * connection in one direction, and we can continue to receive data
- * indefinitely.
- */
- int
- close_tcp(struct tcb *tcb)
- {
- if(tcb == NULLTCB){
- Net_error = INVALID;
- return -1;
- }
- switch(tcb->state){
- case TCP_CLOSED:
- return 0; /* Unlikely */
- case TCP_LISTEN:
- case TCP_SYN_SENT:
- close_self(tcb,NORMAL);
- return 0;
- case TCP_SYN_RECEIVED:
- case TCP_ESTABLISHED:
- tcb->sndcnt++;
- tcb->snd.nxt++;
- setstate(tcb,TCP_FINWAIT1);
- tcp_output(tcb);
- return 0;
- case TCP_CLOSE_WAIT:
- tcb->sndcnt++;
- tcb->snd.nxt++;
- setstate(tcb,TCP_LAST_ACK);
- tcp_output(tcb);
- return 0;
- case TCP_FINWAIT1:
- case TCP_FINWAIT2:
- case TCP_CLOSING:
- case TCP_LAST_ACK:
- case TCP_TIME_WAIT:
- Net_error = CON_CLOS;
- return -1;
- }
- return -1; /* "Can't happen" */
- }
-
- /* Delete TCB, free resources. The user is not notified, even if the TCB is
- * not in the TCP_CLOSED state. This function should normally be called by the
- * user only in response to a state change upcall to TCP_CLOSED state.
- */
- int
- del_tcp(struct tcb *conn)
- {
- struct tcb *tcb, *tcblast = NULLTCB;
- struct reseq *rp, *rp1;
-
- /* Remove from list */
- for(tcb=Tcbs;tcb != NULLTCB;tcblast = tcb,tcb = tcb->next)
- if(tcb == conn)
- break;
- if(tcb == NULLTCB){
- Net_error = INVALID;
- return -1; /* conn was NULL, or not on list */
- }
- if(tcblast != NULLTCB)
- tcblast->next = tcb->next;
- else
- Tcbs = tcb->next; /* was first on list */
-
- stop_timer(&tcb->timer);
- for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
- rp1 = rp->next;
- free_p(rp->bp);
- xfree((char *)rp);
- }
- tcb->reseq = NULLRESEQ;
- free_p(tcb->rcvq);
- free_p(tcb->sndq);
- xfree((char *)tcb);
- return 0;
- }
-
- /* Return 1 if arg is a valid TCB, 0 otherwise */
- int
- tcpval(struct tcb *tcb)
- {
- struct tcb *tcb1;
-
- if(tcb == NULLTCB)
- return 0; /* Null pointer can't be valid */
-
- for(tcb1 = Tcbs; tcb1 != NULLTCB; tcb1 = tcb1->next){
- if(tcb1 == tcb)
- return 1;
- }
- return 0;
- }
-
- /* Kick a particular TCP connection */
- int
- kick_tcp(struct tcb *tcb)
- {
- if(!tcpval(tcb))
- return -1;
-
- tcb->flags.force = 1;
- tcp_timeout(tcb);
- return 0;
- }
-
- /* Kick all TCP connections to specified address; return number kicked */
- int
- kick(int32 addr)
- {
- struct tcb *tcb;
- int cnt = 0;
-
- for(tcb=Tcbs; tcb != NULLTCB; tcb = tcb->next){
- if(tcb->conn.remote.address == addr){
- kick_tcp(tcb);
- cnt++;
- }
- }
- return cnt;
- }
-
- /* Clear all TCP connections */
- void
- reset_all()
- {
- struct tcb *tcb, *tcbtmp = NULLTCB;
-
- for(tcb = Tcbs; tcb != NULLTCB; tcb = tcbtmp) {
- tcbtmp = tcb->next;
- reset_tcp(tcb);
- pwait(NULL); /* Let the RSTs go forth */
- }
- }
-
- void
- reset_tcp(tcb)
- struct tcb *tcb;
- {
- struct tcp fakeseg;
- struct ip fakeip;
-
- if(tcb == NULLTCB)
- return;
- if(tcb->state != TCP_LISTEN){
- /* Compose a fake segment with just enough info to generate the
- * correct RST reply
- */
- fakeseg.flags.rst = 0;
- fakeseg.dest = tcb->conn.local.port;
- fakeseg.source = tcb->conn.remote.port;
- fakeseg.flags.ack = 1;
- /* Here we try to pick a sequence number with the greatest likelihood
- * of being in his receive window.
- */
- fakeseg.ack = tcb->snd.nxt + tcb->snd.wnd - 1;
- fakeip.dest = tcb->conn.local.address;
- fakeip.source = tcb->conn.remote.address;
- fakeip.tos = tcb->tos;
- reset(&fakeip,&fakeseg);
- }
- close_self(tcb,RESET);
- }
-